#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <errno.h> // 定义了全局错误码 errno
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/sysmacros.h>

#define SIZE_1K 1024
#define SIZE_1M 1024*1024
#define SIZE_10M 10*1024*1024

const char* get_basename(const char* path) {
    const char* last_slash = strrchr(path, '/');
    return (last_slash && last_slash != path) ? last_slash + 1 : path;
}

void cp_File(int fd_src, int fd_dst){
    int src_len = lseek(fd_src, 0, SEEK_END);
    if(src_len < 0){
        perror("获取文件大小失败");
        close(fd_src);
        close(fd_dst);
        return;
    }
    lseek(fd_src, 0, SEEK_SET);
    int size;
    if(src_len <= SIZE_1M)size = SIZE_1K;
    if(SIZE_1M <= src_len && src_len <= 512*SIZE_1M)size = SIZE_1M;
    if(512*SIZE_1M <= src_len)size = SIZE_10M;
    char *buf = malloc(size);
    if(!buf){
        perror("内存分配失败");
        close(fd_src);
        close(fd_dst);
        return;
    }
    while(1){
        int read_n = read(fd_src, buf, size);
        if(read_n > 0){
            int write_n = write(fd_dst, buf, read_n);
            if(write_n < 0){
                printf("写入失败\n");
                break;
            }
            int total = read_n - write_n;
            while(total)total -= write(fd_dst, buf + read_n - total, total);
        }
        else if(read_n == 0)
            break;
        else{
            printf("拷贝失败\n");
            break;
        }
    }
    printf("文件拷贝完毕\n");
    close(fd_src);
    close(fd_dst);
    free(buf);
}

void cp_Dir(const char *src_file, const char *dst_file){
    char ori_path[100] = {0};
    char src_path[100] = {0};
    char dst_path[100] = {0};
    getcwd(ori_path, 100);
    chdir(src_file);
    getcwd(src_path, 100);
    chdir(ori_path);
    mkdir(dst_file, 0755);
    chdir(dst_file);
    getcwd(dst_path, 100);
    chdir(src_path);
    printf("ori_path:%s\n", ori_path);
    printf("src_path:%s\n", src_path);
    printf("dst_path:%s\n", dst_path);

    struct dirent *ep;
    DIR *dp = opendir(src_path);
    if(dp == NULL){
        perror("打开源目录失败");
        return;
    }
    while(1){
        struct stat st;
        bzero(&st, sizeof(st));
        ep = readdir(dp);
        if(ep == NULL)break;
        if(strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)continue;
        stat(ep->d_name, &st);
        if(S_ISREG(st.st_mode)){
            int fd_src = open(ep->d_name, O_RDONLY);
            chdir(dst_path);
            int fd_dst = open(ep->d_name, O_RDWR|O_CREAT|O_TRUNC, 0644);
            chdir(src_path);
            cp_File(fd_src, fd_dst);
        }
        else if(S_ISDIR(st.st_mode)){
            chdir(dst_path);
            mkdir(ep->d_name, 0755);
            chdir(ep->d_name);
            char path[100] = {0};
            getcwd(path, 100);
            chdir(src_path);
            cp_Dir(ep->d_name, path);
        }
        else{
            printf("[%s]该文件暂不支持拷贝!\n", ep->d_name);
            continue;
        }
    }
}

int main(int argc, char const *argv[])
{
    if(argc != 3){
        printf("参数数量错误\n");
        return 0;
    }
    struct stat st;
    stat(argv[1], &st);
    if(S_ISREG(st.st_mode)){
        int fd_src = open(argv[1], O_RDONLY);
        if(fd_src < 0){
            printf("找不到源文件路径\n");
            return -1;
        }
        int fd_dst = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0644);
        if(fd_dst < 0){
            printf("找不到目标路径\n");
            return -1;
        }
        cp_File(fd_src, fd_dst);
    }
    else if((S_ISDIR(st.st_mode))){
        // char dst_path[100];
        // snprintf(dst_path, 100, "%s/%s", argv[2], get_basename(argv[1]));
        // printf("目标路径:%s\n", dst_path);
        // cp_Dir(argv[1], dst_path);
        cp_Dir(argv[1], argv[2]);
    }
    else
        printf("该文件类型暂不支持拷贝\n");
}
